/**
 *	The NeuroCoSA Toolkit
 *	Copyright (C) 2003-6 Stuart Meikle.
 *
 *	This is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	Lesser General Public License for more details.
 *
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 * @license	LGPL
 */

package org.stumeikle.NeuroCoSA;
import java.util.*;

/** A DPU networker. Almost a DPU itself but for the messiness of getting things
 *  in and out. Kind of a graph. 
 */
public class	DPUMachine
{
    Vector<DPU>		iComponents; /** the dpus in this machine in order  */
    DPU			iInput;
    DPU			iOutput;
    int			iNumLayers;
    
    DPUMachine()
    { 
	iComponents = new Vector<DPU>();
	iInput = new DPU();	//not really necessary
	iOutput = new DPU();
	iNumLayers = 1;
    }

    //would be good if we can implement organise and max-path lenght routines here
    public	void	addDPU( DPU d )
    {
	iComponents.add(d); 
    }

    public	Vector<DPU>	getComponents()
    { return iComponents; } 

    public	void	connectToOutput( DPU d )
    {
	iOutput.addInput(d);
    }

    public	void	connectToInput( DPU d )
    {
	d.addInput(iInput);
    }

    public	void	rationalise()
    {
	//compute the graph and re-organise the update sequence to best fit
	//start at the output
	iOutput.setDepth(0.0);

	iterateDepth( iOutput );
    }

    private	void	iterateDepth( DPU d )
    {
	double	base = d.getDepth();

	base = base + 10.0;
	ListIterator	i = d.getInputs().listIterator();
	for(;i.hasNext();)
 	{
	    DPU		id = (DPU) i.next();
	
	    if (id.getDepth()==-1.0 || id.getDepth()>base)
	    {
		id.setDepth(base);
		iterateDepth( id );
	    }
	}
    }

    public	long	computeDepth()
    {
	//find the max depth
	//should be the input but you never know
	double 	maxd = iInput.getDepth();
	ListIterator	i=iComponents.listIterator();

	for(;i.hasNext();)
	{
	    DPU		d = (DPU)i.next();
	    if (d.getDepth() > maxd)
		maxd = d.getDepth();
	}

	return (long)(maxd/10.0);
    }

    public	void	sortComponents()
    {
	//rationalise must be called first
	Collections.sort( iComponents );
	Collections.reverse(iComponents);
    }
    
    public	DPU	getOutput()
    {
	return iOutput;
    }

    public	DPU	getInput()
    {
	return iInput;
    }

    public	long	getNumDPUs()
    {
	return (long) iComponents.size();
    }

    public	DataBlock	update(DataBlock ip)
    {
	//move the ip into the input DPU
	//update the components
	//return the db in the outut DPU
	iInput.setOutput( ip );//don't think about it
	ListIterator	i=iComponents.listIterator();

	for(;i.hasNext();)
	{
	    DPU		d = (DPU)i.next();
	    d.update();

// 	    System.out.println("Updating cortex, " + d.getName() + " output =" + d.getOutput());
	}
	
	return iOutput.getInputs().getFirst().getOutput();
    }

    public static void	main( String[] argv )
    {
	DPU	a = new DPU();
	DPU	b = new DPU();
	DPU 	c = new DPU();
	DPU 	d = new DPU();
	DPU 	e = new DPU();
	DPU 	f = new DPU();
	DPU 	g = new DPU();
	DPU 	h = new DPU();
	DPU 	i = new DPU();
	DPU 	j = new DPU();

	a.addInput(b);
	f.addInput(a);
	f.addInput(d);
	b.addInput(e);
	b.addInput(f);
	c.addInput(f);
	c.addInput(b);
	i.addInput(h);
	i.addInput(g);
	j.addInput(c);
	j.addInput(i);
	
	DPUMachine	m = new DPUMachine();
	m.addDPU(a);
	m.addDPU(b);
	m.addDPU(c);
	m.addDPU(d);
	m.addDPU(e);
	m.addDPU(f);
	m.addDPU(g);
	m.addDPU(h);
	m.addDPU(i);
	m.addDPU(j);
	m.connectToOutput(j);
	m.connectToInput(a);

	m.rationalise();
	m.sortComponents();
	System.out.println("Max depth was " + m.computeDepth());

	//m.update( someKindOfInput );
	DataBlock	retval = m.getOutput().getOutput();
    }
}

